<?php

namespace App\Utility;

use App\Utility\Pool\RedisObject;
use EasySwoole\EasySwoole\Config;

class RedisClient{
    protected $redis;
    protected $config = [];

    public function __construct(RedisObject $redisObj){
        $this->redis = $redisObj;
        $options = Config::getInstance()->getConf('REDIS');
        $this->config = $options;
    }

    //设置访问的DB库
    public function connection($db){
        $this->redis->select((int)$db);
        return $this;
    }

    //设置key的过期时间，key过期后将不再可用。单位以秒计。
    public function expire($key, $integer){
        return $this->redis->expire($this->getCacheKey($key), $integer);
    }

    //以秒为单位返回 key 的剩余过期时间
    //当 key 不存在时，返回 -2 。 当 key 存在但没有设置剩余生存时间时，返回 -1 。 否则，以秒为单位，返回 key 的剩余生存时间
    public function ttl($key){
        return $this->redis->ttl($this->getCacheKey($key));
    }

    //设置指定 key 的值
    public function set($name, $value, $expire = null){
        $key   = $this->getCacheKey($name);
        $expire= $this->getExpireTime($expire);
        $value = $this->jsonData($value);
        if ($expire) {
            $result = $this->redis->setex($key, $expire, $value);
        } else {
            $result = $this->redis->set($key, $value);
        }
        return $result;
    }

    //获取指定 key 的值。如果 key 不存在时，返回 nil。
    //如果 key 不是字符串类型，那么返回一个错误
    public function get($name, $default = null){
        $key   = $this->getCacheKey($name);
        $value = $this->redis->get($key);
        if (is_null($value) || false === $value) {
            return $default;
        }
        return $this->unJsonData($value);
    }


    //将 key 所储存的值加上给定的增量值
    public function inc($name, $step = 1){
        return $this->redis->incrBy($this->getCacheKey($name), $step);
    }

    //key 所储存的值减去给定的减量值
    public function dec($name, $step = 1){
        return $this->redis->decrBy($this->getCacheKey($name), $step);
    }

    //获取指定 key 的值并删除key。
    public function pull($name){
        $result = $this->get($name);
        if ($result) {
            $this->delete($name);
            return $result;
        }
        return null;
    }

    //删除指定key
    public function delete($name){
        return $this->redis->del($this->getCacheKey($name));
    }

    //检查给定 key 是否存在
    public function has($name){
        return $this->redis->exists($this->getCacheKey($name));
    }

    //清除所有缓存
    public function clear(){
        $keys = $this->redis->keys($this->config['prefix'].':'.'*');
        if ($keys) {
            $this->redis->del(...$keys);
        }
        return true;
    }

    //返回 key 所储存的值的类型。
    public function type($key){
        return $this->redis->type($key);
    }

    //json化数据
    protected function jsonData($value){
        if(is_numeric($value) || is_string($value)){
            return $value;
        }
        return json_encode($value);
    }

    //反json化数据
    protected function unJsonData($value){
        $reault = json_decode($value,true);
        if($reault && is_array($reault)){
            return $reault;
        }
        return $value;

    }
    //设置过期时间，单位秒
    protected function getExpireTime($expire){
        if ($expire instanceof \DateTime) {
            $expire = $expire->getTimestamp() - time();
        }
        return (int) $expire;
    }

    //获取缓存前缀
    protected function getCacheKey($name){
        return $this->config['prefix'].':'.$name;
    }

    //获取缓存前缀
    public function setPrefix($name){
        $this->config['prefix'] = $name;
        return $this;
    }

    /****** set(无序集合) ******/

    //向无序集合中增加一个或多个元素，并设置集合过期时间
    public function sAdd($key,$member){
        $key = $this->getCacheKey($key);
        if(is_array($member)){
            array_walk($member,function (&$item){
                $item = $this->jsonData($item);
            });
            array_unshift($member,$key);
            $result = call_user_func_array([$this->redis, "sAdd"],$member);
        }else{
            $result = $this->redis->sAdd($key, $this->jsonData($member));
        }
        return $result;
    }

    //返回无序集合中的一个或多个随机元素（仅仅返回随机元素，而不对集合进行任何改动）
    //只提供集合 key 参数时，返回一个元素；如果集合为空，返回 nil 。 如果提供了 count 参数，那么返回一个数组；如果集合为空，返回空数组。
    public function sRandMember($key, $count=null){
        $key = $this->getCacheKey($key);
        if($count){
            $result = $this->redis->sRandMember($key,$count);
            if(count($result) == 0)  return null;
            array_walk($result,function (&$item){
                $item = $this->unJsonData($item);
            });
            return $result;
        }else{
            $result = $this->redis->sRandMember($key);
            if(is_null($result)) return null;
            return $this->unJsonData($result);
        }
    }

    //获取无序集合的成员数(当集合key不存在时，返回0)
    public function scard($key){
        return $this->redis->scard($this->getCacheKey($key));
    }

    //移除并返回无序集合中的一个随机元素
    public function sPop($key){
        $result = $this->redis->sPop($this->getCacheKey($key));
        if(is_null($result)){
            return null;
        }
        return $this->unJsonData($result);
    }

    //判断 member 元素是否是无序集合 key 的成员(如果成员元素是集合的成员，返回 1 。 如果成员元素不是集合的成员，或 key 不存在，返回 0 )
    public function sismember($key, $member){
        return $this->redis->sismember($this->getCacheKey($key), $member);
    }

    //返回无序集合中的所有成员(不存在的集合 key 被视为空集合。)
    public function sMembers($key){
        //需要处理
        $result = $this->redis->sMembers($this->getCacheKey($key));
        if(count($result)){
            array_walk($result,function (&$item){
                $item = $this->unJsonData($item);
            });
        }
        return $result;
    }

    //移除无序集合中一个或多个成员(返回被成功移除的元素的数量)
    public function srem($key,$member){
        $key = $this->getCacheKey($key);
        if(is_array($member)){
            array_unshift($member,$key);
            return call_user_func_array([$this->redis, "srem"],$member);
        }
        return $this->redis->srem($key,$member);
    }

    //所有给定集合的并集存储在 destination 集合中(复制一个集合到新的集合)
    public function sUnionStore($dst, $key, $other_keys){
        return $this->redis->sUnionStore($dst, $this->getCacheKey($key), $this->getCacheKey($other_keys));
    }



    /****** sorted set(有序集合，分用于排行榜、过期时间、固定ID等场景) ******/

    //向有序集合中增加一个或多个元素，或更新集合中一个值得分数（分数值可以是整数值或双精度浮点数）
    public function zAdd($key, $score, $member){
        return $this->redis->zAdd($this->getCacheKey($key), $score, $member);
    }

    //统计有序集合成员数，当 key 不存在时，返回 0 。
    public function zCard($key){
        return $this->redis->zCard($this->getCacheKey($key));
    }

    //统计有序集合中指定分数区间的成员数量。
    public function zCount($key, $min, $max){
        return $this->redis->zCount($this->getCacheKey($key), $min, $max);
    }

    //有序集合中指定成员的分数加上增量 increment(如果increment为负数，则为减法，分数值可以是整数值或双精度浮点数)
    //当 key 不存在，或分数不是 key 的成员时，等同于 ZADD key score member 。
    public function zIncrBy($key, $increment, $member){
        return $this->redis->zIncrBy($this->getCacheKey($key), $increment, $member);
    }

    //通过字典区间返回有序集合指定区间内的成员。（显示分数$scores='WITHSCORES'）
    //如果key不存在，返回空数组，如果key不是有序集合，返回false
    public function zRange($key, $start, $end, $scores=null){
        return $this->redis->zRange($this->getCacheKey($key), $start, $end, $scores);
    }

    //通过分数区间返回有序集合中指定分数区间的成员列表(此方法并不能使用，原因暂时未知)
    public function zRangeByScore($key, $start, $end, $options=null){
        return $this->redis->zRangeByScore($this->getCacheKey($key), $start, $end, $options);
    }

    //移除有序集中的一个或多个成员
    public function zrem($key,$member){
        $key = $this->getCacheKey($key);
        if(is_array($member)){
            array_unshift($member,$key);
            return call_user_func_array([$this->redis, "zrem"],$member);
        }
        return $this->redis->zrem($key,$member);
    }

    //移除有序集中，指定分数（score）区间内的所有成员。
    public function zRemRangeByScore($key, $min, $max){
        return $this->redis->zRemRangeByScore($this->getCacheKey($key), $min, $max);
    }

    //返回有序集中指定区间内的成员，通过索引，分数从高到底（WITHSCORES）
    public function zRevRange($key, $start, $end, $scores=null){
        return $this->redis->zRevRange($this->getCacheKey($key), $start, $end, $scores);
    }

    //返回有序集中成员的排名。其中有序集成员按分数值递减(从大到小)排序
    public function zRevRank($key, $member){
        $result = $this->redis->zRevRank($this->getCacheKey($key), $member);
        if(is_null($result)){
            return 0;
        }
        return $result;
    }

    /****** list队列 ******/

    //设置队列名称
    public function onQueue($name){
        $this->config['queue'] = 'queue:'.$name;
        return $this;
    }

    //入列,将一个值插入到列表的尾部
    public function push($data){
        return $this->redis->rPush($this->config['queue'],$this->jsonData($data));
    }
    //入列,将多个值插入到列表的尾部
    public function pushMulti($data){
        $key = $this->config['queue'];
        if(!is_array($data)){
            return false;
        }
        array_walk($data,function (&$item){
            $item = $this->jsonData($item);
        });
        array_unshift($data,$key);
        return call_user_func_array([$this->redis, 'rPush'],$data);
    }

    //出列,移出并获取列表的第一个元素
    public function read(){
        $key = $this->config['queue'];
        $data = $this->redis->lPop($key);
        if (is_null($data)) {
            return null;
        }
        return $this->unJsonData($data);
    }

    //返回列表的长度。 如果列表 key 不存在，则 key 被解释为一个空列表，返回 0 。 如果 key 不是列表类型，返回一个错误。
    public function lLen(){
        $key = $this->config['queue'];
        $result = $this->redis->lLen($key);
        if (is_null($result) || $result===false) {
            return null;
        }
        return $result;
    }



}
